package jflowmap.views.heatmap;

import java.awt.Color;
import java.awt.Font;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import java.text.DecimalFormat;

import jflowmap.data.ValueStatistics;
import jflowmap.model.ontology.Context;
import jflowmap.model.ontology.Dimension;
import jflowmap.model.ontology.Selection;
import jflowmap.model.ontology.SelectionListener;
import jflowmap.model.ontology.Value;
import jflowmap.util.ColorUtils;
import prefuse.util.ColorLib;
import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolox.util.PFixedWidthStroke;

public class HeatMapCell extends PPath implements SelectionListener {
	public final static double CELL_HEIGHT = 25;
	private final static Stroke BORDER_STROKE = new PFixedWidthStroke(0);
	private final static Font CAPTION_FONT = new Font("Arial", Font.PLAIN, 9);
	
	private final PText text;
	private final Value value;
	private final Dimension valueDimension;
	private final SingleHeatMapRow singleHeatMapRow;
	private final String heatmapName;
	
	private final HeatMapLayer heatMap;
	
	public HeatMapCell(SingleHeatMapRow singleRow, Value value, Dimension dimension, HeatMapLayer heatMap) {
		this.value = value;
		this.singleHeatMapRow = singleRow;
		this.valueDimension = dimension;
		this.heatMap = heatMap;
		this.heatmapName = heatMap.getName() + ":ordering";
		
		if (value == Context.missingValue) {
			text = new PText(" ");
		} else {
			String number = new DecimalFormat("##0.00").format(value.getValue());
			if (number.length() > 6)
				text = new PText("+");
			else
				text = new PText(number);
		}
		
		this.addChild(text);
		text.setFont(CAPTION_FONT);
		text.setX(3);
		text.setY(8);
		
		this.setBounds(0, 0, text.getWidth()+6, CELL_HEIGHT);
		updateColor();
		
		heatMap.getView().getSelection(heatmapName).addSelectionListener(valueDimension, this);
		
		this.addInputEventListener(new PBasicInputEventHandler() {
			private boolean selected = false;
			
			@Override
			public void mouseClicked(PInputEvent event) {
				final Selection ordering = HeatMapCell.this.heatMap.getView().getSelection(heatmapName);
				
				if (ordering.isSelected("SOURCE", singleHeatMapRow.getSourceDimension())
					&& ordering.isSelected("VALUE", valueDimension)) {
					
					ordering.unselect("VALUE", valueDimension);
					ordering.unselect("SOURCE", singleHeatMapRow.getSourceDimension());
					
				} else { 
					ordering.empty("VALUE");
					ordering.empty("SOURCE");
					
					ordering.select("SOURCE", singleHeatMapRow.getSourceDimension());
					ordering.select("VALUE", valueDimension);
				}
				
				HeatMapCell.this.heatMap.getVisualHeatMap().update();
			}
		});
	}

	private void updateColor() {
		if (value == Context.missingValue) {
			this.setPaint(Color.LIGHT_GRAY);
			
		} else {
			final ValueStatistics stats = heatMap.getValueStatistics(value.getMeasurement());
			double val = stats.normalizer().normalizeLogAroundZero(value.getValue(), true);
			
			if (stats.isDiverging()) {
				if (val < -1.0 || val > 1.0) {
					this.setPaint(Color.green);  // out of color scale
				}
				// use diverging color scheme
				this.setPaint(ColorLib.getColor(ColorUtils.colorFromMap(VisualHeatMap.divergingColorScheme.getColors(),
						val, -1.0, 1.0, 255, true)));
			} else {
				if (val < 0.0 || val > 1.0) {
					this.setPaint(Color.green);
				}
				// use sequential color scheme
				this.setPaint(ColorLib.getColor(ColorUtils.colorFromMap(VisualHeatMap.sequentialColorScheme.getColors(),
						val, 0.0, 1.0, 255, true)));
			}
		}
	}

	public void setCellWidth(double cellWidth) {
		this.setPathTo(new Rectangle2D.Double(0, 0, cellWidth, CELL_HEIGHT));
		this.setStroke(BORDER_STROKE);
		text.setX(cellWidth/2 - text.getWidth()/2);
	}
	
	public Value getValue() {
		return value;
	}

	public void update() {
		// TODO Auto-generated method stub
		
	}

	public Dimension getValueDimension() {
		return valueDimension;
	}

	@Override
	public void select(String category, Dimension dimension) {
		if ("VALUE".equals(category)) {	
			final Selection ordering = heatMap.getView().getSelection(heatmapName);
			
			if (singleHeatMapRow.getSourceDimension().equals(ordering.get("SOURCE").iterator().next())) {
				this.heatMap.orderBy(ordering.get("SOURCE").iterator().next(), getValueDimension());
				
				this.heatMap.getInfo().set(heatMap.getGraph().getCommonTargetDimension().get("plural", String.class) + ":",
						heatMap.getRowComparator().getDescription());
				this.moveToFront();
			}
		}
		
	}

	@Override
	public void unselect(String category, Dimension dimension) {
		if ("VALUE".equals(category)) {
			final Selection ordering = heatMap.getView().getSelection(heatmapName);
			
			if (singleHeatMapRow.getSourceDimension().equals(ordering.get("SOURCE").iterator().next())) {
				this.heatMap.orderByName();
				this.heatMap.getInfo().set(heatMap.getGraph().getCommonTargetDimension().get("plural", String.class) + ":", 
						heatMap.getRowComparator().getDescription());
				this.moveToBack();
			}
		}
	}

	@Override
	public void dispose() {
		heatMap.getView().getSelection(heatmapName).removeSelectionListener(valueDimension, this);
	}
}
